
#include "ChemFormula.hpp"
#include "ChemUtils.hpp"
#include <vector>
#include <algorithm>
#pragma once
namespace Chemistry {

    class EquationPosition {
    protected:
        MolecularFormula  *_formula;
        int _count;
    public:
        EquationPosition(MolecularFormula* formula) : _count(1) {
            _formula = formula;
        }
        MolecularFormula * getMolecularFormula(){
        	return _formula;
		}
		
		void SetCount(int count){
			_count = count;
		}
		std::vector<std::string> getElements() {
		 	return _formula->getElements();
		}
		virtual int getElementCount(const char* element) {
            return _count * _formula->getElementCount(element);
        }
        
        std::string getString(){
        	std::string str = "";
        	if (_count > 1) {
        		str += getCountString(_count);
        	}
        	str += _formula->getString();
        	return str;
		}
    };
	
	class Nofity{
	public:
		virtual bool OnChange() = 0; 
	};
	
	
    class EquationPositions {
    protected:
        std::vector<EquationPosition*> _formulas;
    public:
        size_t getFormulasCount() const {
            return _formulas.size();
        }
        
        EquationPosition* getFormula(size_t index) const {
            if (index < _formulas.size()) {
                return _formulas[index];
            }
            return nullptr;
        }
        void append(EquationPosition* pos) {
            _formulas.push_back(pos);
        }
        std::vector<std::string> getElements() {
        	std::vector<std::string> out;
        	for (size_t i = 0; i < _formulas.size(); i++)
            {
                std::vector<std::string> newItem =_formulas[i]->getElements();
                for(size_t j = 0; j < newItem.size(); j++)
                {
                	std::vector<std::string>::iterator it = std::find(out.begin(), out.end(), newItem[j]);
                    if (it == out.end())
                    {
                        out.push_back(newItem[j]);
                    }
				}
            }
		 	return out;
		}
		
		virtual int getElementCount(const char* element) {
			int cnt = 0;
            for (size_t i = 0; i < _formulas.size(); i++)
            {
                cnt += _formulas[i]->getElementCount(element);
            }
            return cnt;
        }
		
		int getTopCount(){
			int topCount = 0;
			std::vector<std::string> elements = getElements();
			for (size_t i = 0; i < elements.size(); i++){
				int newCount = getElementCount(elements[i].c_str());
			 	if (newCount>topCount){
			 		topCount = newCount;
				}
			}
			return topCount;
		}
		
        bool equal(EquationPositions & positions){
        	std::vector<std::string> elements1 = getElements();
        	std::vector<std::string> elements2 = positions.getElements();
        	if (elements1.size() != elements2.size()){
        		return false;
			}
        	for (size_t i = 0; i < elements1.size(); i++){
        		std::vector<std::string>::iterator it = std::find(elements2.begin(), elements2.end(), elements1[i]);
                if (it == elements2.end())
                {
                    return false;
                }
                int cnt1 = getElementCount((*it).c_str());
                int cnt2 = positions.getElementCount((*it).c_str());
                if (cnt1 != cnt2){
                	return false;
				}
			}
			return true;
		}
		
		bool tryCount(int maxCount,Nofity * notify){
			for (size_t i = 0; i < _formulas.size(); i++) {
            	for(int j=1;j<maxCount;j++) {
            		_formulas[i]->SetCount(j);
            		if(notify->OnChange())
            		{
            			return true;
					}
				}
            }
            return false;
		}
		std::string getString(){
			std::string out ;
			for (size_t i = 0; i < _formulas.size(); i++)
			{
				out+= _formulas[i]->getString(); 
			}
			return out;
		}
    };
	
    class Equation :public Nofity{
    protected:
        EquationPositions _left;
        EquationPositions _right;
        virtual bool OnChange(){
        	return _left.equal(_right);
		}
	public:
		Equation(){
		}
		void appendLeft(EquationPosition *pos){
			_left.append(pos);
		}
		void appendRight(EquationPosition *pos) {
			_right.append(pos);
		}
        bool Balance() {
			return balanceEquation();
        }
        
    private:
        bool balanceEquation() {
            const int MAX_COEFF = 8;
            std::vector<int> leftCoeffs(_left.getFormulasCount(), 1);
            std::vector<int> rightCoeffs(_right.getFormulasCount(), 1);
            
            return tryBalance(leftCoeffs, rightCoeffs, 0, 0, MAX_COEFF);
        }
        
        bool tryBalance(std::vector<int>& leftCoeffs, std::vector<int>& rightCoeffs, 
                       int leftIndex, int rightIndex, int maxCoeff) {
            if (leftIndex == leftCoeffs.size() && rightIndex == rightCoeffs.size()) {
                return checkBalance(leftCoeffs, rightCoeffs);
            }
            
            if (leftIndex < leftCoeffs.size()) {
                for (int i = 1; i <= maxCoeff; i++) {
                    leftCoeffs[leftIndex] = i;
                    if (tryBalance(leftCoeffs, rightCoeffs, leftIndex + 1, rightIndex, maxCoeff)) {
                        return true;
                    }
                }
            } else if (rightIndex < rightCoeffs.size()) {
                for (int i = 1; i <= maxCoeff; i++) {
                    rightCoeffs[rightIndex] = i;
                    if (tryBalance(leftCoeffs, rightCoeffs, leftIndex, rightIndex + 1, maxCoeff)) {
                        return true;
                    }
                }
            }
            return false;
        }
        
        bool checkBalance(const std::vector<int>& leftCoeffs, const std::vector<int>& rightCoeffs) {
            std::vector<std::string> allElements = getAllElements();
            
            for (const std::string& element : allElements) {
                int leftCount = 0, rightCount = 0;
                
                for (size_t i = 0; i < _left.getFormulasCount(); i++) {
                    leftCount += leftCoeffs[i] * _left.getFormula(i)->getMolecularFormula()->getElementCount(element.c_str());
                }
                
                for (size_t i = 0; i < _right.getFormulasCount(); i++) {
                    rightCount += rightCoeffs[i] * _right.getFormula(i)->getMolecularFormula()->getElementCount(element.c_str());
                }
                
                if (leftCount != rightCount) {
                    return false;
                }
            }
            
            // 如果平衡，应用系数
            for (size_t i = 0; i < _left.getFormulasCount(); i++) {
                _left.getFormula(i)->SetCount(leftCoeffs[i]);
            }
            for (size_t i = 0; i < _right.getFormulasCount(); i++) {
                _right.getFormula(i)->SetCount(rightCoeffs[i]);
            }
            
            return true;
        }
        
        std::vector<std::string> getAllElements() {
            std::vector<std::string> elements;
            std::vector<std::string> leftElements = _left.getElements();
            std::vector<std::string> rightElements = _right.getElements();
            
            for (const std::string& element : leftElements) {
                if (std::find(elements.begin(), elements.end(), element) == elements.end()) {
                    elements.push_back(element);
                }
            }
            
            for (const std::string& element : rightElements) {
                if (std::find(elements.begin(), elements.end(), element) == elements.end()) {
                    elements.push_back(element);
                }
            }
            return elements;
        }
        
    public:
        void print(){
        	std::string o = _left.getString() + " = " + _right.getString();
        	std::cout<< o << std::endl; 
		}
    };
}
